package dwarf

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"gddgame.cc/galaxy/utils/cache"
	"gddgame.cc/galaxy/utils/def"
	"github.com/mitchellh/go-homedir"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

type RootContext struct {
	def.Logger
	def.Store
}

func (context *RootContext) ID() string {
	return "cmd"
}

var (
	child = true
	iFn   func() error
	pFns  = []func() error{}
	pErr  error
)
var Context = &RootContext{}

// rootCmd represents the base command when called without any subcommands
var Root = &RootCommand{
	Command: &cobra.Command{
		Use:   filepath.Base(os.Args[0]),
		Short: "",
		Long:  "",
		Run: func(cmd *cobra.Command, args []string) {
			// 自动进入主程序逻辑
			child = false
			//fmt.Println("Root run")
		},
		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
			//fmt.Println("PostRun", child, pFns)
			if pErr != nil {
				return pErr
			}
			if !child {
				// 启动
				if pFns != nil {
					for k := range pFns {
						if err := pFns[k](); err != nil {
							return err
						}
					}
				}
			}
			return nil
		},
	},
}

type RootCommand struct {
	*cobra.Command

	def.Env

	debug      bool
	test       bool
	daemon     bool
	version    bool
	pidFile    string
	logLevel   string
	configFile string

	configName string
	configPath []string

	versionInfo string
}

func (root *RootCommand) BindEnv(env def.Env) {
	root.Env = env
	Context.Logger = env.Logger()
	c := cache.Memory(Context.Logger)
	Context.Store = c.Store("cmd")
}

func init() {
	cobra.OnInitialize(initConfig)
}

// 添加子命令
func AddCommand(command *cobra.Command) {
	Root.AddCommand(command)
}

func SetInfo(subject string, description string, version string) {
	Root.Short = subject
	Root.Long = description
	Root.versionInfo = version
}

// 开启config加载
// path:
//     ~ home directory
//     . root directory
func BindBase(name string, paths []string) {
	Root.configName = name
	Root.configPath = paths
	Root.Flags().BoolVarP(&Root.debug, "debug", "D", false, "Enable debug mode")
	Root.Flags().BoolVarP(&Root.test, "test", "t", false, "Test server start and exit")
	Root.Flags().BoolVarP(&Root.daemon, "daemon", "", false, "Run Daemon mode")
	Root.Flags().BoolVarP(&Root.version, "version", "", false, "Show Current Version")
	Root.PersistentFlags().StringVarP(&Root.logLevel, "log-level", "l", "info", "Set the logging level")
	Root.PersistentFlags().StringVar(&Root.configFile, "config", "", fmt.Sprintf("Location of config file：%s json/yaml", name))
}

func BindPid(pid string) {
	Root.Flags().StringVarP(&Root.pidFile, "pid", "p", pid, "Server pid file")
}

func PersistentPostRun(i func() error, fns ...func() error) {
	iFn = i
	pFns = fns
}

func initConfig() {
	if Root.logLevel != "" {
		if err := Root.SetLogLevel(Root.logLevel); err != nil {
			Root.Println(err)
		}
	}
	if Root.version {
		fmt.Println(Root.versionInfo)
		os.Exit(0)
		return
	}
	if Root.debug {
		Root.SetDebug()
	}
	if Root.test {
		Root.SetTest()
	}
	if Root.daemon {
		Root.SetDaemon()
	}
	if Root.pidFile != "" {
		Root.SetPidFile(Root.pidFile)
	}
	if Root.configName != "" {
		if Root.configFile != "" {
			// Use config file from the flag.
			viper.SetConfigFile(Root.configFile)
		} else {
			for _, p := range Root.configPath {
				if p == "~" {
					// Find home directory.
					home, err := homedir.Dir()
					if err != nil {
						pErr = err
						return
					}

					// Search config in home directory with name ".project" (without extension).
					//fmt.Println(home)
					viper.AddConfigPath(home)
				} else if p == "." {
					dir, err := filepath.Abs(filepath.Dir(os.Args[0])) //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
					if err != nil {
						pErr = err
						return
					}
					p = strings.Replace(dir, "\\", "/", -1) //将\替换成/
					//fmt.Println(p)
					viper.AddConfigPath(p)
				} else {
					//fmt.Println(p)
					viper.AddConfigPath(p)
				}
			}
			currentPath, _ := os.Getwd()
			viper.AddConfigPath(currentPath)
			viper.SetConfigName(Root.configName)
		}

		viper.AutomaticEnv() // read in environment variables that match
		// If a config file is found, read it in.
		if err := viper.ReadInConfig(); err == nil {
			//Root.Println("Using config file:", viper.ConfigFileUsed())
			if err := Root.SetConfig(viper.AllSettings()); err != nil {
				pErr = err
				return
			}
		}
	}

	if iFn != nil {
		if err := iFn(); err != nil {
			pErr = err
			return
		}
	}
}
